package main

import (
	"fmt"
	"github.com/casbin/casbin/v2"
	"github.com/casbin/casbin/v2/model"
	gormadapter "github.com/casbin/gorm-adapter/v3"
	"gorm.io/driver/mysql"
	"gorm.io/gorm"
	"gorm.io/gorm/logger"
	"time"
)

// check 权限检查函数
// 功能：验证指定用户对特定资源是否有操作权限（支持缓存执行器）
// 参数：
//   - e: 带缓存的Casbin执行器实例
//   - sub: 主体（用户或角色）
//   - obj: 资源对象
//   - act: 操作类型
func check(e *casbin.CachedEnforcer, sub, obj, act string) {
	// 使用带缓存的Casbin执行器进行权限验证
	// 缓存机制可提高频繁权限检查的性能
	ok, _ := e.Enforce(sub, obj, act)

	// 根据权限检查结果输出相应信息
	if ok {
		fmt.Printf("%s CAN %s %s\n", sub, act, obj)
	} else {
		fmt.Printf("%s CANNOT %s %s\n", sub, act, obj)
	}
}

// InitGorm 初始化GORM数据库连接
// 功能：创建并配置MySQL数据库连接，设置连接池参数
// 返回值：配置好的GORM数据库实例
func InitGorm() *gorm.DB {
	// 数据库连接字符串（DSN）
	// 格式：用户名:密码@协议(地址:端口)/数据库名?参数
	dsn := "root:root@tcp(127.0.0.1:3306)/rule_db?charset=utf8mb4&parseTime=True&loc=Local"

	var mysqlLogger logger.Interface

	// 连接数据库
	db, err := gorm.Open(mysql.Open(dsn), &gorm.Config{
		Logger:                                   mysqlLogger,
		DisableForeignKeyConstraintWhenMigrating: true, // 迁移时禁用外键约束，避免外键依赖问题
	})

	if err != nil {
		// 连接失败时记录错误并终止程序
		panic(err)
	}

	// 获取底层SQL连接池用于配置连接参数
	sqlDB, _ := db.DB()

	// 配置连接池参数以优化数据库性能
	sqlDB.SetMaxIdleConns(10)               // 最大空闲连接数，减少连接建立开销
	sqlDB.SetMaxOpenConns(100)              // 最大打开连接数，控制并发连接数
	sqlDB.SetConnMaxLifetime(time.Hour * 4) // 连接最大复用时间，避免长时间连接问题

	return db
}

// DB 全局数据库实例变量
var DB *gorm.DB

// main 程序主入口
// 功能：演示Casbin与GORM集成，实现基于数据库的权限持久化
func main() {
	// 初始化数据库连接
	DB = InitGorm()

	// 数据库迁移（当前注释掉，需要时取消注释）
	// DB.AutoMigrate(&gormadapter.CasbinRule{})

	// 使用GORM适配器创建Casbin适配器
	// 将权限策略存储在MySQL数据库中
	a, _ := gormadapter.NewAdapterByDB(DB)

	// 从文件加载Casbin权限模型
	// 模型文件定义权限控制逻辑和规则
	m, err := model.NewModelFromFile("model.pml")
	if err != nil {
		// 权限模型加载失败处理
		// logger.Error("权限模型加载失败！", err)
		return
	}

	// 创建带缓存的Casbin执行器
	// 缓存机制可显著提高权限检查性能
	e, _ := casbin.NewCachedEnforcer(m, a)

	// 设置缓存过期时间为1小时（60 * 60秒）
	// 过期后自动重新加载策略，确保权限数据及时更新
	e.SetExpireTime(60 * 60)

	// 加载权限策略到内存缓存
	_ = e.LoadPolicy()

	// 权限策略管理演示
	// 添加权限策略：admin角色可以GET /api/users
	e.AddPolicy("admin", "/api/users", "GET")

	// 为用户zhangsan分配admin角色
	e.AddRoleForUser("zhangsan", "admin")

	// 检查zhangsan是否有权限GET /api/users（应该有权限）
	check(e, "zhangsan", "/api/users", "GET")

	// 动态权限调整演示
	// 移除zhangsan的admin角色
	e.RemoveGroupingPolicy("zhangsan", "admin")

	// 移除admin角色对/api/users的GET权限
	e.RemovePolicy("admin", "/api/users", "GET")

	// 再次检查权限（此时应该无权限）
	check(e, "zhangsan", "/api/users", "GET")

	// 保存策略更改到数据库持久化存储
	e.SavePolicy()

	// 最终权限检查
	check(e, "zhangsan", "/api/users", "GET")
}
